﻿using System;
using System.IO;
using System.Net;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NAnt.Core;
using NAnt.Core.Attributes;
using NAnt.Core.Types;
using System.Data.OleDb;
using System.Collections;
using System.Web;

namespace NAntExt.Tasks
{

    public class FormValueCollection : List<FormValue>
    {
    }

    /// <summary>
    /// The form values for the <see cref="PostTask"/>.
    /// </summary>
    [Serializable]
    [ElementName("value")]
    public class FormValue : Element
    {
        /// <summary>
        /// Initializes a new instance of this class.
        /// </summary>
        public FormValue()
        {
            // default to encode to true
            this.Encode = true;
        }

        /// <summary>
        /// The key of the form value.
        /// </summary>
        [TaskAttribute("key", Required = true)]
        [StringValidator(AllowEmpty = false)]
        public string Key { get; set; }

        /// <summary>
        /// The value of the form item with the name.
        /// </summary>
        [TaskAttribute("value", Required = true)]
        [StringValidator(AllowEmpty = true)]
        public string Value { get; set; }

        /// <summary>
        /// Determines whether or not to url encode the value. Default is true.
        /// </summary>
        [TaskAttribute("encode", Required = false)]
        [BooleanValidator()]
        public bool Encode { get; set; }
    }

    /// <summary>
    /// Posts values to a url and returns the results.
    /// </summary>
    [TaskName("http-request")]
    public class HttpRequstTask : Task
    {
        /// <summary>
        /// The url to post the values.
        /// </summary>
        [TaskAttribute("url", Required = true)]
        [StringValidator(AllowEmpty = false)]
        public string Url { get; set; }

        /// <summary>
        /// The property to place the output of the form post.
        /// </summary>
        [TaskAttribute("outputProperty", Required = false)]
        [StringValidator(AllowEmpty = true)]
        public string OutputProperty { get; set; }

        /// <summary>
        /// The method of request. Default is GET.
        /// </summary>
        [TaskAttribute("method", Required = false)]
        [StringValidator(AllowEmpty = true)]
        public string Method { get; set; }

        /// <summary>
        /// The commas seperated list of status codes that indicate a success. Default is 200.
        /// </summary>
        [TaskAttribute("successStatusCodes", Required = false)]
        [StringValidator(AllowEmpty = true)]
        public string SuccessStatusCodes { get; set; }

        /// <summary>
        /// The values to send in the request.
        /// </summary>
        [BuildElementCollection("form", "value", ElementType = typeof(FormValue))]
        public FormValue[] Values { get; set; }

        /// <summary>
        /// Entry point for the task.
        /// </summary>
        protected override void ExecuteTask()
        {
            try
            {
                
                var request = WebRequest.Create(this.Url);
                request.Method = (string.IsNullOrEmpty(Method)) ? "GET" : Method;
                
                var formValues = new StringBuilder();
                if (Values != null && Values.Length > 0)
                {
                    request.ContentType = "application/x-www-form-urlencoded";

                    foreach (FormValue value in Values)
                    {
                        if (formValues.Length > 0)
                            formValues.Append("&");

                        formValues.Append(HttpUtility.UrlEncode(value.Key))
                            .Append("=");

                        if (value.Encode)
                        {
                            formValues.Append(HttpUtility.UrlEncode(value.Value));
                        }
                        else
                        {
                            formValues.Append(value.Value);
                        }
                    }

                    // set the length of the request
                    request.ContentLength = formValues.Length;

                    // write the values to the request stream
                    using (var sw = new StreamWriter(request.GetRequestStream()))
                    {
                        sw.Write(formValues);
                    }
                }
                
                // get the response
                if (Verbose)
                {
                    Log(Level.Verbose, "Http request started. Url: {0}, Method: {1}, Content: {2}", this.Url, request.Method, formValues);
                }
                var timer = new System.Diagnostics.Stopwatch();
                timer.Start();
                HttpWebResponse response;
                try
                {
                    response = (HttpWebResponse)request.GetResponse();
                }
                catch (WebException ex)
                {
                    // ignore webexceptions
                    response = (HttpWebResponse)ex.Response;
                }                
                timer.Stop();

                // always set the outputProperty if it's set just incase failure is acceptable
                string responseContent;
                try
                {
                    using (var sr = new StreamReader(response.GetResponseStream()))
                    {
                        responseContent = sr.ReadToEnd();
                        
                    }
                }
                finally
                {
                    response.Close();
                }
                if (!string.IsNullOrEmpty(OutputProperty))
                {
                    this.Properties[OutputProperty] = responseContent;
                }

                if ((string.IsNullOrEmpty(SuccessStatusCodes) && response.StatusCode == HttpStatusCode.OK)
                    || (!string.IsNullOrEmpty(SuccessStatusCodes) && SuccessStatusCodes.Split(',').Any(statusCode => statusCode == Convert.ToInt32(response.StatusCode).ToString())))
                {
                    // success status code received
                    
                    // do nothing; just continue

                    if (Verbose)
                    {
                        Log(Level.Verbose, "Http request succeed. Elapsed: {0}", timer.Elapsed);
                    }
                }
                else
                {
                    // failed status code received

                    throw new System.ApplicationException(string.Format("Http request to failed. Url: {0}, Method: {1}, Status: {2}, Response: {3}", this.Url, this.Method, response.StatusCode, responseContent));
                }
            }
            catch (Exception ex)
            {
                this.ThrowOrLog(this.Location, ex);
            }
        }

    }
}
